﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Controls;
using YArchitech.Revit;
using YJKArchBaseCode.OutWallSearch;
using YJKGeometryFuncs;

namespace YJKRevitDimensionText.DoorWindowDim
{
	public class DoorWindowDimension
	{
		public DoorWindowDimension(ExternalCommandData cmdData)
		{
			this.m_doc = cmdData.Application.ActiveUIDocument.Document;
			this.m_view = this.m_doc.ActiveView;
			this.m_manager = new DoorWindowDimensionManager(cmdData);
		}

		public void CreateElementDimension()
		{
			try
			{
				List<Element> effectiveElements = this.GetEffectiveElements();
				if (effectiveElements == null)
				{
					YJKMessageBox.Information("没有可标注的门窗实例");
				}
				else
				{
					List<List<Element>> levelGroupLists = this.GetLevelGroupLists(effectiveElements);
					List<List<Element>> list = this.ElementGroups(levelGroupLists, effectiveElements);
					list.Sort(new ElementGroupComparer(this.m_view.ViewDirection));
					List<Level> allLevels = this.m_manager.GetAllLevels();
					List<List<Element>> lstDimensionGroups = this.DimensionGroups(list, allLevels);
					this.m_manager.CreateDimension(lstDimensionGroups);
				}
			}
			catch
			{
			}
		}

		private List<Element> GetEffectiveElements()
		{
			List<ViewPlan> allFloorPlans = this.GetAllFloorPlans();
			if (0 >= allFloorPlans.Count)
			{
				return null;
			}
			List<FamilyInstance> disjointElements = this.GetDisjointElements(allFloorPlans);
			if (0 >= disjointElements.Count)
			{
				return null;
			}
			List<Element> list = new List<Element>();
			foreach (FamilyInstance item in disjointElements)
			{
				list.Add(item);
			}
			list.Sort(new ElementComparer());
			return list;
		}

		private List<FamilyInstance> GetDisjointElements(List<ViewPlan> lstFloorPlans)
		{
			List<FamilyInstance> result = new List<FamilyInstance>();
			List<Curve> lstOutWallCurves = null;
			ProcessBarForm processBarForm = new ProcessBarForm();
			processBarForm.Show();
			processBarForm.SetText("立面门窗标注进度");
			processBarForm.SetProcessBarMax(lstFloorPlans.Count - 1);
			processBarForm.SetProcessBarNowValue(0);
			Transaction transaction = new Transaction(this.m_doc);
			transaction.Start("Modify Autodesk.Revit.DB.View Underlay");
			this.DeleteSplitCurve();
			for (int i = 0; i < lstFloorPlans.Count; i++)
			{
				try
				{
					processBarForm.SetProcessBarNowValue(i);
					ViewPlan viewPlan = lstFloorPlans.ElementAt(i);
					BaseLineInfo baseLineParameter = RevitElementExtension.GetBaseLineParameter(viewPlan);
					BaseLineInfo baseLineInfo = new BaseLineInfo
					{
						BaseLineID = ElementId.InvalidElementId,
						TopLevelID = ElementId.InvalidElementId,
						BaseLineDirection = 0
					};
					RevitElementExtension.SetBaseLineParameter(viewPlan, baseLineInfo);
					lstOutWallCurves = new List<Curve>();
					List<Wall> currentFloorOutWalls = this.GetCurrentFloorOutWalls(viewPlan, ref lstOutWallCurves);
					List<FamilyInstance> instancesOnWall = this.m_manager.GetInstancesOnWall(currentFloorOutWalls);
					if (instancesOnWall == null || 0 >= instancesOnWall.Count)
					{
						RevitElementExtension.SetBaseLineParameter(viewPlan, baseLineParameter);
					}
					else
					{
						List<FamilyInstance> lsFilterElems = this.FilteredRedundantElements(instancesOnWall);
						this.GetLineDisjointEntities(lsFilterElems, lstOutWallCurves, ref result);
						RevitElementExtension.SetBaseLineParameter(viewPlan, baseLineParameter);
					}
				}
				catch (Exception)
				{
					if (1 == (int)transaction.GetStatus())
					{
						transaction.RollBack();
					}
				}
			}
			transaction.RollBack();
			processBarForm.Close();
			return result;
		}

		private void DeleteSplitCurve()
		{
			List<ElementId> list = new List<ElementId>();
			new List<Curve>();
			foreach (Element element in new FilteredElementCollector(this.m_doc).OfCategory(BuiltInCategory.OST_RoomSeparationLines).WhereElementIsNotElementType().ToElements())
			{
				if (((element as ModelCurve).LineStyle as GraphicsStyle).Name == "<房间分隔>")
				{
					list.Add(element.Id);
				}
			}
			this.m_doc.Delete(list);
		}

		private List<Wall> GetCurrentFloorOutWalls(ViewPlan viewPlan, ref List<Curve> lstOutWallCurves)
		{
			List<Wall> list = new List<Wall>();
			List<Wall> list2 = new List<Wall>();
			OutWallFilter.Search(this.m_doc, viewPlan.Id, ref list2, true, false);
			foreach (Wall wall in list2)
			{
				Curve curve = (wall.Location as LocationCurve).Curve;
				if (typeof(Line) == curve.GetType())
				{
					Line line = curve as Line;
					XYZ xyz = new XYZ(line.GetEndPoint(0).X, line.GetEndPoint(0).Y, 0.0);
					XYZ xyz2 = new XYZ(line.GetEndPoint(1).X, line.GetEndPoint(1).Y, 0.0);
					curve = Line.CreateBound(xyz, xyz2);
				}
				else if (typeof(Arc) == curve.GetType())
				{
					Arc arc = curve as Arc;
					XYZ xyz3 = new XYZ(arc.GetEndPoint(0).X, arc.GetEndPoint(0).Y, 0.0);
					XYZ xyz4 = new XYZ(arc.GetEndPoint(1).X, arc.GetEndPoint(1).Y, 0.0);
					XYZ xyz5 = arc.Evaluate(0.5, true);
					curve = Arc.Create(xyz3, xyz4, xyz5);
				}
				lstOutWallCurves.Add(curve);
				list.Add(wall);
			}
			return list;
		}

		private List<FamilyInstance> FilteredRedundantElements(List<FamilyInstance> lstDoorWindowElemIds)
		{
			List<FamilyInstance> list = new List<FamilyInstance>();
			foreach (FamilyInstance familyInstance in lstDoorWindowElemIds)
			{
				double num = familyInstance.FacingOrientation.Normalize().AngleTo(this.m_view.ViewDirection);
				if (1E-06 <= Math.Abs(num - Math.PI*.5))
				{
					list.Add(familyInstance);
				}
			}
			return list;
		}

		private void GetLineDisjointEntities(List<FamilyInstance> lsFilterElems, List<Curve> lstOutWallCurves, ref List<FamilyInstance> lstDisjointElems)
		{
			List<int> list = new List<int>();
			foreach (FamilyInstance familyInstance in lstDisjointElems)
			{
				list.Add(familyInstance.Id.IntegerValue);
			}
			foreach (FamilyInstance familyInstance2 in lsFilterElems)
			{
				bool flag = false;
				XYZ instanceLocationPoint = this.m_manager.GetInstanceLocationPoint(familyInstance2);
				XYZ xyz = new XYZ(instanceLocationPoint.X, instanceLocationPoint.Y, 0.0);
				XYZ xyz2 = xyz + this.m_view.ViewDirection * this.m_dRayLength;
				Line line = Line.CreateBound(xyz, xyz2);
				foreach (Curve curve in lstOutWallCurves)
				{
					IntersectionResultArray intersectionResultArray = new IntersectionResultArray();
					line.Intersect(curve, out intersectionResultArray);
					if (intersectionResultArray != null && 0 < intersectionResultArray.Size)
					{
						for (int i = 0; i < intersectionResultArray.Size; i++)
						{
							if (!intersectionResultArray.get_Item(i).XYZPoint.IsAlmostEqualTo(xyz))
							{
								flag = true;
								break;
							}
						}
					}
				}
				if (!flag && !list.Contains(familyInstance2.Id.IntegerValue))
				{
					list.Add(familyInstance2.Id.IntegerValue);
					lstDisjointElems.Add(familyInstance2);
				}
			}
		}

		private List<List<Element>> ElementGroups(List<List<Element>> lstLevelGroups, List<Element> lstElements)
		{
			List<List<Element>> list = new List<List<Element>>();
			List<Element> list2 = new List<Element>();
			list2.AddRange(lstElements);
			foreach (List<Element> list3 in lstLevelGroups)
			{
				foreach (Element element in list3)
				{
					if (this.IsExistingElement(element, list2))
					{
						List<Element> list4 = this.RecursiveTraversal(element, ref list2);
						if (list4 != null)
						{
							list.Add(list4);
						}
					}
				}
			}
			return list;
		}

		private List<Element> RecursiveTraversal(Element elem, ref List<Element> lstAllElems)
		{
			List<Element> list = new List<Element>();
			List<Element> collisionGroup = this.GetCollisionGroup(elem, lstAllElems);
			if (0 >= collisionGroup.Count)
			{
				return null;
			}
			list.AddRange(collisionGroup);
			this.RemoveExistedElement(collisionGroup, ref lstAllElems);
			foreach (Element elem2 in collisionGroup)
			{
				List<Element> list2 = this.RecursiveTraversal(elem2, ref lstAllElems);
				if (list2 != null)
				{
					list.AddRange(list2);
				}
			}
			return list;
		}

		private List<Element> GetCollisionGroup(Element elem, List<Element> lstAllElements)
		{
			List<Element> list = new List<Element>();
			BoundingBoxXYZ boundingBoxXYZ = elem.get_BoundingBox(this.m_view);
			double num = Math.Abs(boundingBoxXYZ.Min.X - boundingBoxXYZ.Max.X) * Math.Abs(boundingBoxXYZ.Min.Y - boundingBoxXYZ.Max.Y);
			for (int i = 0; i < lstAllElements.Count; i++)
			{
				Element element = lstAllElements.ElementAt(i);
				BoundingBoxXYZ boundingBoxXYZ2 = element.get_BoundingBox(this.m_view);
				double num2 = Math.Abs(boundingBoxXYZ2.Min.X - boundingBoxXYZ2.Max.X) * Math.Abs(boundingBoxXYZ2.Min.Y - boundingBoxXYZ2.Max.Y);
				BoundingBoxXYZ boundingBoxXYZ3 = Geometry.BoundingBoxesMerge(boundingBoxXYZ, boundingBoxXYZ2);
				if (Math.Abs(boundingBoxXYZ3.Min.X - boundingBoxXYZ3.Max.X) * Math.Abs(boundingBoxXYZ3.Min.Y - boundingBoxXYZ3.Max.Y) < num + num2 && !this.IsExistingElement(element, list))
				{
					list.Add(element);
				}
			}
			return list;
		}

		private void RemoveExistedElement(List<Element> lstRemoveElems, ref List<Element> lstAllElements)
		{
			foreach (Element item in lstRemoveElems)
			{
				if (lstAllElements.Contains(item))
				{
					lstAllElements.Remove(item);
				}
			}
		}

		private List<ViewPlan> GetAllFloorPlans()
		{
			List<Element> list = new FilteredElementCollector(this.m_doc).OfClass(typeof(ViewPlan)).ToList<Element>();
			List<ViewPlan> list2 = new List<ViewPlan>();
			foreach (Element element in list)
			{
				ViewPlan viewPlan = element as ViewPlan;
				if (viewPlan != null && 1 == (int)viewPlan.ViewType && viewPlan.GenLevel != null)
				{
					list2.Add(viewPlan);
				}
			}
			return list2;
		}

		private List<List<Element>> GetLevelGroupLists(List<Element> lstElements)
		{
			List<List<Element>> list = new List<List<Element>>();
			List<Level> allLevels = this.m_manager.GetAllLevels();
			if (0 < allLevels.Count)
			{
				foreach (Level level in allLevels)
				{
					List<Element> list2 = new List<Element>();
					foreach (Element element in lstElements)
					{
						if (level.Id == element.LevelId)
						{
							list2.Add(element);
						}
					}
					list.Add(list2);
				}
			}
			return list;
		}

		private bool IsExistingElement(Element elemCheck, List<Element> lstElements)
		{
			bool result = false;
			foreach (Element element in lstElements)
			{
				if (elemCheck.Id == element.Id)
				{
					result = true;
					break;
				}
			}
			return result;
		}

		private List<List<Element>> DimensionGroups(List<List<Element>> lstCollisionGroups, List<Level> lstLevels)
		{
			if (0 >= lstCollisionGroups.Count && 0 >= lstLevels.Count)
			{
				return null;
			}
			this.FilterHeightAndPositionSameGroups(ref lstCollisionGroups);
			List<List<Element>> list = new List<List<Element>>();
			for (int i = 0; i < lstCollisionGroups.Count; i++)
			{
				List<Element> item = lstCollisionGroups.ElementAt(i);
				this.InsertIntoListOfLevel(ref item, lstLevels);
				list.Add(item);
			}
			return list;
		}

		private void InsertIntoListOfLevel(ref List<Element> lstElements, List<Level> lstLevels)
		{
			List<Element> list = new List<Element>();
			list.AddRange(lstElements);
			for (int i = 0; i < lstLevels.Count; i++)
			{
				Level level = lstLevels.ElementAt(i);
				double elevation = level.Elevation;
				List<Element> aboveLevelOfElements = this.GetAboveLevelOfElements(list, elevation);
				if (0 < aboveLevelOfElements.Count)
				{
					Element element = aboveLevelOfElements.ElementAt(aboveLevelOfElements.Count - 1);
					double z = (element.Location as LocationPoint).Point.Z;
					int j = 0;
					while (j < lstElements.Count)
					{
						Element element2 = lstElements.ElementAt(j);
						if (element.Id == element2.Id)
						{
							if (!this.ElementAndLevelIntersect(element, level))
							{
								lstElements.Insert(j + 1, level);
								break;
							}
							if (aboveLevelOfElements.Count == list.Count && i < lstLevels.Count - 1)
							{
								lstElements.Insert(j + 1, lstLevels.ElementAt(i + 1));
								break;
							}
							break;
						}
						else
						{
							j++;
						}
					}
					this.RemoveExistedElement(aboveLevelOfElements, ref list);
				}
			}
		}

		private List<Element> GetAboveLevelOfElements(List<Element> lstElements, double dElevation)
		{
			List<Element> list = new List<Element>();
			foreach (Element element in lstElements)
			{
				if (!(typeof(Level) == element.GetType()))
				{
					double z = (element.Location as LocationPoint).Point.Z;
					if (1E-06 < z - dElevation)
					{
						list.Add(element);
					}
					else
					{
						List<Face> dimensionFaces = this.m_manager.GetDimensionFaces((element as FamilyInstance).Host as Wall, element);
						if (2 == dimensionFaces.Count)
						{
							using (List<Face>.Enumerator enumerator2 = dimensionFaces.GetEnumerator())
							{
								while (enumerator2.MoveNext())
								{
									Face face = enumerator2.Current;
									if (1E-06 < (face as PlanarFace).Origin.Z - dElevation)
									{
										list.Add(element);
										break;
									}
								}
								continue;
							}
						}
						if (dimensionFaces.Count > 0)
						{
							double z2 = this.m_manager.OneFaceSituation(element, dimensionFaces.ElementAt(0)).GetEndPoint(0).Z;
							double z3 = (dimensionFaces.ElementAt(0) as PlanarFace).Origin.Z;
							if (1E-06 < z2 - dElevation || 1E-06 < z3 - dElevation)
							{
								list.Add(element);
							}
						}
					}
				}
			}
			return list;
		}

		private bool ElementAndLevelIntersect(Element elem, Level level)
		{
			bool result = false;
			List<double> list = new List<double>();
			Wall wall = (elem as FamilyInstance).Host as Wall;
			List<Face> dimensionFaces = this.m_manager.GetDimensionFaces(wall, elem);
			double num = 0.0;
			double num2 = 0.0;
			if (1 == dimensionFaces.Count)
			{
				Curve curve = this.m_manager.OneFaceSituation(elem, dimensionFaces.ElementAt(0));
				num = (dimensionFaces.ElementAt(0) as PlanarFace).Origin.Z;
				num2 = curve.GetEndPoint(0).Z;
			}
			else if (dimensionFaces.Count > 1)
			{
				foreach (Face face in dimensionFaces)
				{
					PlanarFace planarFace = face as PlanarFace;
					list.Add(planarFace.Origin.Z);
				}
				num = list[0];
				num2 = list[1];
			}
			double elevation = level.Elevation;
			if ((1E-06 < num - elevation && 1E-06 > num2 - elevation) || (1E-06 > num - elevation && 1E-06 < num2 - elevation))
			{
				result = true;
			}
			return result;
		}

		private void FilterHeightAndPositionSameGroups(ref List<List<Element>> lstGroups)
		{
			for (int i = 0; i < lstGroups.Count - 1; i++)
			{
				List<Element> list = lstGroups.ElementAt(i);
				for (int j = i + 1; j < lstGroups.Count; j++)
				{
					List<Element> list2 = lstGroups.ElementAt(j);
					if (list.Count == list2.Count && this.IsSameGroup(list, list2))
					{
						lstGroups.RemoveAt(j);
						j--;
					}
				}
			}
		}

		private bool IsSameGroup(List<Element> lstFirstGroup, List<Element> lstSecondGroup)
		{
			for (int i = 0; i < lstFirstGroup.Count; i++)
			{
				FamilyInstance familyInstance = lstFirstGroup.ElementAt(i) as FamilyInstance;
				ElementId id = familyInstance.Symbol.Id;
				XYZ point = (familyInstance.Location as LocationPoint).Point;
				FamilyInstance familyInstance2 = lstSecondGroup.ElementAt(i) as FamilyInstance;
				ElementId id2 = familyInstance2.Symbol.Id;
				XYZ point2 = (familyInstance2.Location as LocationPoint).Point;
				if (1E-06 < Math.Abs(point.Z - point2.Z))
				{
					return false;
				}
			}
			return true;
		}

		private Document m_doc;

		private Autodesk.Revit.DB.View m_view;

		private DoorWindowDimensionManager m_manager;

		private double m_dRayLength = 10000.0;
	}
}
